#include "stk_message_rtsp.h"
#include "stk_utility.h"
#include "stk_const.h"
#include <string.h>

using namespace stk;

/* 构造 */
RtspMsg::RtspMsg()
{
    init();
}

RtspMsg::RtspMsg(const char *str, int32_t len)
{
    init();

    if (!str || len == 0)
    {
        return;
    }

    parse(str, len);
}

RtspMsg::RtspMsg(const RtspMsg &src)
{
    if (this == &src)
    {
        return;
    }

    init();

    *this = src;
}

RtspMsg::~RtspMsg()
{
    clear();
}

RtspMsg & RtspMsg::operator= (const RtspMsg &src)
{
    if (this == &src)
    {
        return *this;
    }

    clear();

    /* 需显示调用基类拷贝构造 */
    MsgBase::operator=(src);

    hosts.assign(src.hosts, _override_t<Host>());
    connections.assign(src.connections, _override_t<Connection>());

    return *this;
}

void RtspMsg::init()
{
    m_iMsgType = MSG_TYPE_RTSP;
}

void RtspMsg::clear()
{
    hosts.clear();
    connections.clear();
}

int32_t	RtspMsg::to_str(string &str)
{
    int32_t ret = 0;

    str = "";

    if (1 == m_iMessageProperty)
    {
        str = m_strMessage;

        return 0;
    }
    else
    {
        m_strMessage = "";
    }

    if (MakeStartLineString(str) < 0)
    {
        return -1;
    }

    str += CRLF;

    string strColon = ": ";


    string strFieldHeader = STK_COMM_CSEQ + strColon;
    ret += MakeFieldString(cseq, strFieldHeader, str);

    strFieldHeader = STK_COMM_SERVER + strColon;
    ret += MakeFieldString(server, strFieldHeader, str);

    strFieldHeader = STK_COMM_DATE + strColon;
    ret += MakeFieldString(date, strFieldHeader, str);

    strFieldHeader = STK_COMM_PUBLIC + strColon;
    ret += MakeSingleLineFieldString(publics, strFieldHeader, str);

    strFieldHeader = STK_COMM_CONTENT_TYPE + strColon;
    ret += MakeFieldString(content_type, strFieldHeader, str);

    strFieldHeader = STK_COMM_CACHE_CONTROL + strColon;
    ret += MakeFieldString(cache_controls, strFieldHeader, str);

    strFieldHeader = STK_RTSP_TRANSPORT + strColon;
    ret += MakeSingleLineFieldString(transports, strFieldHeader, str);

    strFieldHeader = STK_RTSP_SESSION + strColon;
    ret += MakeFieldString(sessions, strFieldHeader, str);

    strFieldHeader = STK_RTSP_SCALE + strColon;
    ret += MakeFieldString(scales, strFieldHeader, str);

    strFieldHeader = STK_COMM_RANGE + strColon;
    ret += MakeFieldString(ranges, strFieldHeader, str);

    if (ret < 0)
    {
        return -1;
    }

    /* we have to create the body before adding the contentlength */
    /* add enough lenght for "Content-Length: " */

    if (bodies.eol(0))                     /* no body */
    {
        str += CRLF;
        m_iMessageProperty = 1;
        this->m_strMessage = str;
        return 0;                          /* it's all done */
    }

    strFieldHeader = STK_COMM_CONTENT_LENGTH + strColon;
    str += strFieldHeader;

    string strBoundary;
    string strBodyBuffer = CRLF;

    if (!content_type.m_strType.empty() &&
            !strcasecmp(content_type.m_strType.c_str(), "multipart") &&
            bodies.size() >= 2)
    {
#if 0
        /* find the boundary */
        MsgParam *ct_param = content_type.m_listParams.get_byname("boundary");

        if (ct_param && !ct_param->m_strValue.empty())
        {
            size_t len = ct_param->m_strValue.size();

            const int MIME_MAX_BOUNDARY_LEN = 70;

            if (len > MIME_MAX_BOUNDARY_LEN)
            {

                printf("ERRO: <MakeToString> len > MIME_MAX_BOUNDARY_LEN!\n");
                return -1;
            }

            strBoundary =  "--";

            if (len >= 2 && ct_param->m_strValue[0] == '"' && ct_param->m_strValue[len - 1] == '"')
            {
                strBoundary.append(ct_param->m_strValue.c_str() + 1, len - 2);
            }
            else
            {
                strBoundary.append(ct_param->m_strValue.c_str(), len);
            }
        }

#endif
    }

    int pos = 0;

    while (!bodies.eol(pos))
    {
        Body *body = (Body *)bodies.get(pos++);

        if (body == NULL)
        {
            continue;
        }

        string strThisBoundary = strBoundary;

        if (!strThisBoundary.empty())
        {
#if 0
            char szThisBodySize[20] = {0};

            if (pos > 1)
            {
                strThisBoundary = string(CRLF) + strThisBoundary;
            }

            strThisBoundary += CRLF;
            snprintf(szThisBodySize, sizeof(szThisBodySize), "%d\r\n", body->m_strBody.size());
            strThisBoundary += strContentLengthHeader + szThisBodySize ;
            strThisBoundary += strContentTypeHeader;
            body->m_oContentType.to_str(strThisBoundary);
            strThisBoundary += + CRLF ;
            strBodyBuffer += strThisBoundary + CRLF;
#endif
        }

        if (body->to_str(strBodyBuffer) != 0)
        {

            printf("ERRO: <MakeToString> parse body fail!\n");
            return -1;
        }
    }

    if (!strBoundary.empty())
    {
        /* Needs at most 79 bytes,
        last realloc allocate at least 100 bytes extra */
        strBodyBuffer += CRLF + strBoundary + "--" /*+ CRLF*/;
    }

    if (strBodyBuffer.size() <= 2)
    {

        printf("ERRO: <MakeToString> body buffer length <=2!\n");
        return -1;
    }

    char szBodyLen[256] = "";
    snprintf(szBodyLen, sizeof(szBodyLen) - 1, "%ld\r\n", strBodyBuffer.size() - 2);
    str = str + szBodyLen + strBodyBuffer;

    /* same remark as at the beginning of the method */
    m_iMessageProperty = 1;
    m_strMessage       = str;

    return 0;
}

int32_t RtspMsg::parse(const char *str, int32_t len)
{
    if ( len <= 0 || len >= MAX_MESSAGE_SIZE)
    {
        printf("RtspMsg::parse buffer size is 0 or exceeded max size(%d)\n", MAX_MESSAGE_SIZE);

        return -1;
    }

    const char *next_header_index;

    char beg[MAX_MESSAGE_SIZE];
    char *tmp = beg;
    memcpy(tmp, str, len);    /* may contain binary data */
    tmp[len] = '\0';

    stk_util_replace_all_lws(tmp);

    /* parse request or status line */
    if (startline_parse (tmp, &next_header_index) != 0)
    {
        printf("RtspMsg::parse:Can not parse start line.message content=<%s>\n", tmp);

        return -1;
    }

    tmp = (char *)next_header_index;

    /* parse headers */
    if (msg_headers_parse (tmp, &next_header_index) != 0)
    {
        printf("RtspMsg::parse:Can not parse message headers.message content=<%s>\n", tmp);
        return -1;
    }

    tmp = (char *)next_header_index;


    /* this is a *very* simple test... (which handle most cases...) */
    if (tmp[0] == '\0' || tmp[1] == '\0' || tmp[2] == '\0')
    {
        m_bValid = true;
        return 0;                 /* no body found */
        //return SetMsgIdByMethodName();
    }

    if (msg_body_parse(tmp, &next_header_index, len - (tmp - beg)) != 0)
    {
        printf("RtspMsg::parse:Can not parse message body.message content=<%s>\n", tmp);
        return -1;
    }

    m_bValid = true;

    return 0;
}


int32_t RtspMsg::startline_parse (const char *buf, const char **headers)
{
    if (0 == strncmp ((const char *) buf, (const char *) "RTSP/", 5))
    {
        return startline_parseresp (buf, headers);
    }
    else
    {
        return startline_parsereq (buf, headers);
    }
}

int32_t	RtspMsg::startline_parsereq (const char *buf, const char **headers)
{
    const char *p1;
    const char *p2;

    m_strMethod   = "";
    m_iStatusCode = 0;

    *headers = buf;

    /* The first token is the method name: */
    p2 = strchr (buf, ' ');

    if (p2 == NULL)
    {
        return -1;
    }

    if (*(p2 + 1) == '\0' || *(p2 + 2) == '\0')
    {
        return -1;
    }

    if (p2 - buf == 0)
    {
        printf(( "No space allowed here\n"));
        return -1;
    }

    m_strMethod.assign(buf, p2 - buf);

    /* The second token is a sip-url or a uri: */
    p1 = strchr (p2 + 2, ' ');    /* no space allowed inside sip-url */

    if (p1 == NULL)
    {
        printf(( "Uncompliant request-uri\n"));
        m_strMethod = "";
        return -1;
    }

    if (p1 - p2 < 2)
    {
        m_strMethod = "";
        return -1;
    }

    m_strReqUrl.assign(p2 + 1, (p1 - p2 - 1));


    /* find the the version and the beginning of headers */
    {
        const char *hp = p1;


        while ((*hp != '\r') && (*hp != '\n'))
        {
            if (*hp)
            {
                hp++;
            }
            else
            {
                printf(( "No crlf found\n"));
                m_strMethod = "";
                m_strReqUrl = "";
                return -1;
            }
        }

        if (hp - p1 < 2)
        {
            m_strMethod = "";
            m_strReqUrl = "";
            return -1;
        }

        m_strVersion.assign(p1 + 1, hp - p1 - 1);

        if (!strcasecmp(m_strVersion.c_str(), "RTSP/1.0") &&
                !strcasecmp(m_strVersion.c_str(), "RTSP/1.1"))
        {
            printf(("Wrong version number\n"));
        }

        hp++;

        if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0]))
        {
            hp++;
        }

        (*headers) = hp;

    }

    return 0;
}

int32_t	RtspMsg::startline_parseresp (const char *buf, const char **headers)
{
    const char *statuscode;
    const char *reasonphrase;

    m_strReqUrl = "";
    m_strMethod = "";

    *headers = buf;

    statuscode = strchr (buf, ' ');       /* search for first SPACE */

    if (statuscode == NULL)
    {
        return -1;
    }

    string	strVersion = "";
    strVersion.assign(buf, statuscode - *headers);

    if (strcmp(strVersion.c_str(), "RTSP/1.0") != 0 &&
            strcmp(strVersion.c_str(), "RTSP/1.1") != 0 )
    {
        return -1;
    }

    m_strVersion = strVersion;

    reasonphrase = strchr (statuscode + 1, ' ');

    if (reasonphrase == NULL)
    {
        m_strVersion = "";
        return -1;
    }

    if (sscanf (statuscode + 1, "%d", &m_iStatusCode) != 1)
    {
        /* Non-numeric status code */
        return -1;
    }

    if (m_iStatusCode == 0)
    {
        return -1;
    }

    {
        const char *hp = reasonphrase;

        while ((*hp != '\r') && (*hp != '\n'))
        {
            if (*hp)
            {
                hp++;
            }
            else
            {
                printf("No crlf found\n");
                return -1;
            }
        }

        m_strReasonPhrase.assign(reasonphrase + 1, hp - reasonphrase - 1);

        hp++;

        if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0]))
        {
            hp++;
        }

        (*headers) = hp;
    }

    return 0;
}

int32_t RtspMsg::SetTransport(const char *szValue)
{
    if (szValue == NULL || szValue[0] == '\0')
    {
        return 0;
    }

    Transport *transport = new Transport(szValue, strlen(szValue));

    if (NULL == transport)
    {
        return -1;
    }

    if (transports.add(transport) != 0)
    {
        delete transport;
        return -1;
    }

    m_iMessageProperty = 2;

    return 0;
}

int32_t RtspMsg::SetTransport(const Transport &field)
{
    if (!field.isvalid())
    {
        return -1;
    }

    Transport *transport = new Transport(field);

    if (!transport)
    {
        return -1;
    }

    transports.add(transport);
    m_iMessageProperty = 2;

    return 0;
}

string RtspMsg::GetTransport()
{
    string str = "";

    MsgField *field = (MsgField *)transports.get(0);

    if (field)
    {
        field->to_str(str);
    }

    return str;
}

Transport *RtspMsg::GetTransport(int32_t pos)
{
    if (transports.size() <= 0 ||
            (pos < 0 && pos != -1) ||
            pos >= transports.size())
    {
        return NULL;
    }

    if (-1 == pos)
    {
        return (Transport *)transports.get(transports.size() - 1);
    }

    return (Transport *)transports.get(pos);
}

int32_t RtspMsg::SetSession(const char *szValue)
{
    if (sessions.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

int32_t RtspMsg::SetSession(const Session &field)
{
    if (!field.isvalid())
    {
        return -1;
    }

    sessions = field;
    m_iMessageProperty = 2;

    return 0;
}

string RtspMsg::GetSession()
{
    string str;
    sessions.to_str(str);

    return str;
}

int32_t RtspMsg::SetScale(const char *szValue)
{
    if (scales.parse(szValue, strlen(szValue)) < 0)
    {
        return -1;
    }

    m_iMessageProperty = 2;
    return 0;
}

int32_t RtspMsg::SetScale(const Scale &field)
{
    if (!field.isvalid())
    {
        return -1;
    }

    scales = field;
    m_iMessageProperty = 2;

    return 0;
}

string RtspMsg::GetScale()
{
    string str;
    scales.to_str(str);

    return str;
}
